package cn.qu.netty.boot;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HandlerMappingProvider {

	private ApplicationContext applicationContext;

	private Map<String, HandlerMapping> map;

	private List<String> paths = new ArrayList<>();
	
	final static Log log = Log.createLog();

	public HandlerMappingProvider(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
		this.loadHandlerMapping();
		log.log("load handler mapping sucess...");
	}

	public HandlerMapping get(String path, String method) {
		path = path.contains("?") ? path.substring(0, path.indexOf("?")) : path;
		return this.map.get(this.match(path, method));
	}

	private void loadHandlerMapping() {
		map = new HashMap<>();
		List<Object> controllers = applicationContext.getBeanByAnnotation(Controller.class);
		for (Object object : controllers) {
			Method[] methods = object.getClass().getMethods();
			for (Method method : methods) {
				RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
				if (requestMapping != null && !Modifier.isStatic(method.getModifiers())) {
					HandlerMapping handlerMapping = new HandlerMapping(object, requestMapping.value(),
							requestMapping.method(), method);
					String path = requestMapping.value();
					this.paths.add(path);
					String key = AbstractParameterResolver.matchKey(path, handlerMapping.getHttpMethod().name());
					if (map.containsKey(key)) {
						throw new RuntimeException("RequestMapping path duplicate");
					}
					map.put(key, handlerMapping);
				}
			}
		}
	}

	private String match(String path, String method) {
		path = path.endsWith("/") ? path : path + "/";
		for (String match : paths) {
			String subPath = path;
			if (match.contains("{")) {
				subPath = "/";
				int countPathParam = AbstractParameterResolver.countPathParam(match);
				String[] splitPath = AbstractParameterResolver.splitPath(path);
				int j = splitPath.length - countPathParam;
				for (int i = 0; i < j; i++) {
					subPath += splitPath[i] + "/";
				}
				subPath += match.substring(match.indexOf("{"));
			}

			if (match.equals(subPath)) {
				path = match;
				break;
			}
		}
		return AbstractParameterResolver.matchKey(path, method);
	}

}
